Add `cargo rustdoc` for passing arbitrary flags to rustdoc
authorManish Goregaokar <manishsmail@gmail.com>
Wed, 9 Sep 2015 08:46:43 +0000 (14:16 +0530)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 9 Nov 2015 16:36:47 +0000 (08:36 -0800)
15 files changed:
src/bin/bench.rs
src/bin/build.rs
src/bin/cargo.rs
src/bin/doc.rs
src/bin/run.rs
src/bin/rustc.rs
src/bin/rustdoc.rs [new file with mode: 0644]
src/bin/test.rs
src/cargo/core/manifest.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_package.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/toml.rs
tests/test_cargo_rustdoc.rs [new file with mode: 0644]
tests/tests.rs

index 7e658997d285a10b86ad9dcce1c2ad800f2b073f..47f5b9a939b410e79a0ea34a505df872cdffd286 100644 (file)
@@ -84,6 +84,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                                             &options.flag_test,
                                             &options.flag_example,
                                             &options.flag_bench),
+            extra_rustdoc_args: Vec::new(),
             target_rustc_args: None,
         },
     };
index 27bcf57b122e1c5443e26f0178e6adaf30c573d1..92971e77db5d6904c485f9097a3252c0d5d8d9f4 100644 (file)
@@ -81,6 +81,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                                         &options.flag_test,
                                         &options.flag_example,
                                         &options.flag_bench),
+        extra_rustdoc_args: Vec::new(),
         target_rustc_args: None,
     };
 
index 144e020a02881f8574e1a55ab6a314e7a8d76a7c..3c31678284d4b33a2efefb73eb7196719c0f2aa6 100644 (file)
@@ -81,6 +81,7 @@ macro_rules! each_subcommand{ ($mac:ident) => ({
     $mac!(read_manifest);
     $mac!(run);
     $mac!(rustc);
+    $mac!(rustdoc);
     $mac!(search);
     $mac!(test);
     $mac!(uninstall);
index 16fd12430ecdb5e2f710443bb84b485c90a8b3a9..0ac61d6bd8255705876978f65657b4eb51729bca 100644 (file)
@@ -70,6 +70,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                 deps: !options.flag_no_deps,
             },
             target_rustc_args: None,
+            extra_rustdoc_args: Vec::new(),
         },
     };
 
index d765967ddda8ae2fa8588ab407b5e9b533bb838f..d74b5d45720eb5b063b18b4ad80618891f3f322d 100644 (file)
@@ -80,6 +80,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                 bins: &bins, examples: &examples,
             }
         },
+        extra_rustdoc_args: Vec::new(),
         target_rustc_args: None,
     };
 
index 924cfb6365e142111f3e12739320880a14bae23e..78a17dadaa53411bfdc21bc30667abc2c27aca1d 100644 (file)
@@ -84,6 +84,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                                         &options.flag_test,
                                         &options.flag_example,
                                         &options.flag_bench),
+        extra_rustdoc_args: Vec::new(),
         target_rustc_args: options.arg_opts.as_ref().map(|a| &a[..]),
     };
 
diff --git a/src/bin/rustdoc.rs b/src/bin/rustdoc.rs
new file mode 100644 (file)
index 0000000..181cbf8
--- /dev/null
@@ -0,0 +1,89 @@
+use cargo::ops;
+use cargo::util::{CliResult, CliError, Config};
+use cargo::util::important_paths::{find_root_manifest_for_cwd};
+
+#[derive(RustcDecodable)]
+struct Options {
+    arg_opts: Vec<String>,
+    flag_target: Option<String>,
+    flag_features: Vec<String>,
+    flag_jobs: Option<u32>,
+    flag_manifest_path: Option<String>,
+    flag_no_default_features: bool,
+    flag_no_deps: bool,
+    flag_open: bool,
+    flag_verbose: bool,
+    flag_release: bool,
+    flag_quiet: bool,
+    flag_color: Option<String>,
+    flag_package: Option<String>,
+}
+
+pub const USAGE: &'static str = "
+Build a package's documentation, using specified custom flags.
+
+Usage:
+    cargo rustdoc [options] [--] [<opts>...]
+
+Options:
+    -h, --help               Print this message
+    --open                   Opens the docs in a browser after the operation
+    -p SPEC, --package SPEC  Package to document
+    --no-deps                Don't build documentation for dependencies
+    -j N, --jobs N           The number of jobs to run in parallel
+    --release                Build artifacts in release mode, with optimizations
+    --features FEATURES      Space-separated list of features to also build
+    --no-default-features    Do not build the `default` feature
+    --target TRIPLE          Build for the target triple
+    --manifest-path PATH     Path to the manifest to document
+    -v, --verbose            Use verbose output
+    -q, --quiet              No output printed to stdout
+    --color WHEN             Coloring: auto, always, never
+
+By default the documentation for the local package and all dependencies is
+built. The output is all placed in `target/doc` in rustdoc's usual format.
+
+The specified target for the current package (or package specified by SPEC if
+provided) will be documented along with all of its dependencies. The specified
+<opts>... will all be passed to the final rustdoc invocation, not any of the
+dependencies. Note that rustdoc will still unconditionally receive
+arguments such as -L, --extern, and --crate-type, and the specified <opts>...
+will simply be added to the rustdoc invocation.
+
+If the --package argument is given, then SPEC is a package id specification
+which indicates which package should be documented. If it is not given, then the
+current package is documented. For more information on SPEC and its format, see
+the `cargo help pkgid` command.
+";
+
+pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
+    try!(config.shell().set_verbosity(options.flag_verbose, options.flag_quiet));
+    try!(config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..])));
+
+    let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path));
+
+    let mut doc_opts = ops::DocOptions {
+        open_result: options.flag_open,
+        compile_opts: ops::CompileOptions {
+            config: config,
+            jobs: options.flag_jobs,
+            target: options.flag_target.as_ref().map(|t| &t[..]),
+            features: &options.flag_features,
+            no_default_features: options.flag_no_default_features,
+            spec: options.flag_package.as_ref().map(|s| &s[..]),
+            exec_engine: None,
+            filter: ops::CompileFilter::Everything,
+            release: options.flag_release,
+            mode: ops::CompileMode::Doc {
+                deps: !options.flag_no_deps,
+            },
+            extra_rustdoc_args: options.arg_opts,
+            target_rustc_args: None,
+        },
+    };
+
+    try!(ops::doc(&root, &mut doc_opts).map_err(|err| CliError::from_boxed(err, 101)));
+
+    Ok(None)
+}
+
index 09981e0bed8541bc8eba2cbb997d05cb87f43930..16c3e1fb3f7997d70aebdf747c91b4006333c136 100644 (file)
@@ -90,6 +90,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                                             &options.flag_test,
                                             &options.flag_example,
                                             &options.flag_bench),
+            extra_rustdoc_args: Vec::new(),
             target_rustc_args: None,
         },
     };
index 68f3d2d4c1d9036e5ae624b7cc0fee89470e668a..ef9fa5433fc845ddc5f6a1af2c51e66dcab52d28 100644 (file)
@@ -112,6 +112,7 @@ pub struct Profile {
     pub lto: bool,
     pub codegen_units: Option<u32>,    // None = use rustc default
     pub rustc_args: Option<Vec<String>>,
+    pub rustdoc_args: Option<Vec<String>>,
     pub debuginfo: bool,
     pub debug_assertions: bool,
     pub rpath: bool,
@@ -474,6 +475,7 @@ impl Default for Profile {
             lto: false,
             codegen_units: None,
             rustc_args: None,
+            rustdoc_args: None,
             debuginfo: false,
             debug_assertions: false,
             rpath: false,
index 547aa8d379039bb7b59e4d6035c3e445307b4eec..0484d8023fec116476d646bc07d0536460ed6482 100644 (file)
@@ -57,6 +57,8 @@ pub struct CompileOptions<'a> {
     pub release: bool,
     /// Mode for this compile.
     pub mode: CompileMode,
+    /// Extra arguments to be passed to rustdoc (for main crate and dependencies)
+    pub extra_rustdoc_args: Vec<String>,
     /// The specified target will be compiled with all the available arguments,
     /// note that this only accounts for the *final* invocation of rustc
     pub target_rustc_args: Option<&'a [String]>,
@@ -145,6 +147,7 @@ pub fn compile_pkg<'a>(root_package: &Package,
     let CompileOptions { config, jobs, target, spec, features,
                          no_default_features, release, mode,
                          ref filter, ref exec_engine,
+                         ref extra_rustdoc_args,
                          ref target_rustc_args } = *options;
 
     let target = target.map(|s| s.to_string());
@@ -223,6 +226,24 @@ pub fn compile_pkg<'a>(root_package: &Package,
     }
 
     let mut ret = {
+    let mut target_with_rustdoc = None;
+    if !extra_rustdoc_args.is_empty() {
+        let mut target_with_rustdoc_inner = Vec::new();
+        for &(target, profile) in &targets {
+            if profile.doc {
+                let mut profile = profile.clone();
+                profile.rustdoc_args = Some(extra_rustdoc_args.clone());
+                target_with_rustdoc_inner.push((target, profile));
+            }
+        }
+        target_with_rustdoc = Some(target_with_rustdoc_inner);
+    };
+
+    let targets = target_with_rustdoc.as_ref().map_or(targets,
+                                             |o| o.into_iter()
+                                                  .map(|&(t, ref p)| (t, p))
+                                                  .collect());
+    let ret = {
         let _p = profile::start("compiling");
         let mut build_config = try!(scrape_build_config(config, jobs, target));
         build_config.exec_engine = exec_engine.clone();
index 635aaf5c34ec7caf7af1941deb73e51dd2e10cdd..45b966b59aa874363a412712fcd951c4886b00c4 100644 (file)
@@ -220,6 +220,7 @@ fn run_verify(config: &Config, pkg: &Package, tar: &Path)
         exec_engine: None,
         release: false,
         mode: ops::CompileMode::Build,
+        extra_rustdoc_args: Vec::new(),
         target_rustc_args: None,
     }));
 
index 5feb3359195639f44579dafa951e4895c97c42a2..2b738bc94358c51613b414425cfdb16c318fddd6 100644 (file)
@@ -395,6 +395,10 @@ fn rustdoc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
         }
     }
 
+    if let Some(ref args) = profile.rustdoc_args {
+        rustdoc.args(args);
+    }
+
     try!(build_deps_args(&mut rustdoc, cx, unit));
 
     if unit.pkg.has_custom_build() {
@@ -448,6 +452,7 @@ fn build_base_args(cx: &Context,
     let Profile {
         opt_level, lto, codegen_units, ref rustc_args, debuginfo,
         debug_assertions, rpath, test, doc: _doc, run_custom_build,
+        rustdoc_args: _,
     } = *unit.profile;
     assert!(!run_custom_build);
 
index 430bdde9a9df9392b0771c4faaa6bb8e9bc3a067..85a7befc0b8b0157fb0cbcba7d434ed3cd0c414a 100644 (file)
@@ -941,6 +941,7 @@ fn build_profiles(profiles: &Option<TomlProfiles>) -> Profiles {
             lto: lto.unwrap_or(profile.lto),
             codegen_units: codegen_units,
             rustc_args: None,
+            rustdoc_args: None,
             debuginfo: debug.unwrap_or(profile.debuginfo),
             debug_assertions: debug_assertions.unwrap_or(profile.debug_assertions),
             rpath: rpath.unwrap_or(profile.rpath),
diff --git a/tests/test_cargo_rustdoc.rs b/tests/test_cargo_rustdoc.rs
new file mode 100644 (file)
index 0000000..775051f
--- /dev/null
@@ -0,0 +1,178 @@
+use std::path::MAIN_SEPARATOR as SEP;
+use support::{execs, project};
+use support::{COMPILING, RUNNING};
+use hamcrest::{assert_that};
+
+fn setup() {
+}
+
+
+test!(rustdoc_simple {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/lib.rs", r#" "#);
+
+    assert_that(p.cargo_process("rustdoc").arg("-v"),
+                execs()
+                .with_status(0)
+                .with_stdout(format!("\
+{compiling} foo v0.0.1 ({url})
+{running} `rustdoc src{sep}lib.rs --crate-name foo \
+        -o {dir}{sep}target{sep}doc \
+        -L dependency={dir}{sep}target{sep}debug \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps`
+",
+            running = RUNNING, compiling = COMPILING, sep = SEP,
+            dir = p.root().display(), url = p.url())));
+});
+
+test!(rustdoc_args {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/lib.rs", r#" "#);
+
+    assert_that(p.cargo_process("rustdoc").arg("-v").arg("--").arg("--no-defaults"),
+                execs()
+                .with_status(0)
+                .with_stdout(format!("\
+{compiling} foo v0.0.1 ({url})
+{running} `rustdoc src{sep}lib.rs --crate-name foo \
+        -o {dir}{sep}target{sep}doc \
+        --no-defaults \
+        -L dependency={dir}{sep}target{sep}debug \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps`
+",
+            running = RUNNING, compiling = COMPILING, sep = SEP,
+            dir = p.root().display(), url = p.url())));
+});
+
+
+
+test!(rustdoc_foo_with_bar_dependency {
+    let foo = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [dependencies.bar]
+            path = "../bar"
+        "#)
+        .file("src/lib.rs", r#"
+            extern crate bar;
+            pub fn foo() {}
+        "#);
+    let bar = project("bar")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "bar"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/lib.rs", r#"
+            pub fn baz() {}
+        "#);
+    bar.build();
+
+    assert_that(foo.cargo_process("rustdoc").arg("-v").arg("--").arg("--no-defaults"),
+                execs()
+                .with_status(0)
+                .with_stdout(format!("\
+{compiling} bar v0.0.1 ({url})
+{running} `rustc {bar_dir}{sep}src{sep}lib.rs [..]`
+{running} `rustdoc {bar_dir}{sep}src{sep}lib.rs --crate-name bar \
+        -o {dir}{sep}target{sep}doc \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps`
+{compiling} foo v0.0.1 ({url})
+{running} `rustdoc src{sep}lib.rs --crate-name foo \
+        -o {dir}{sep}target{sep}doc \
+        --no-defaults \
+        -L dependency={dir}{sep}target{sep}debug \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps \
+        --extern [..]`
+",
+            running = RUNNING, compiling = COMPILING, sep = SEP,
+            dir = foo.root().display(), url = foo.url(),
+            bar_dir = bar.root().display())));
+});
+
+test!(rustdoc_only_bar_dependency {
+    let foo = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [dependencies.bar]
+            path = "../bar"
+        "#)
+        .file("src/main.rs", r#"
+            extern crate bar;
+            fn main() {
+                bar::baz()
+            }
+        "#);
+    let bar = project("bar")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "bar"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/lib.rs", r#"
+            pub fn baz() {}
+        "#);
+    bar.build();
+
+    assert_that(foo.cargo_process("rustdoc").arg("-v").arg("-p").arg("bar")
+                                            .arg("--").arg("--no-defaults"),
+                execs()
+                .with_status(0)
+                .with_stdout(format!("\
+{compiling} bar v0.0.1 ({url})
+{running} `rustdoc {bar_dir}{sep}src{sep}lib.rs --crate-name bar \
+        -o {dir}{sep}target{sep}doc \
+        --no-defaults \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps \
+        -L dependency={dir}{sep}target{sep}debug{sep}deps`
+",
+            running = RUNNING, compiling = COMPILING, sep = SEP,
+            dir = foo.root().display(), url = foo.url(),
+            bar_dir = bar.root().display())));
+});
+
+
+test!(rustdoc_same_name_err {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/main.rs", r#"
+            fn main() {}
+        "#)
+        .file("src/lib.rs", r#" "#);
+
+    assert_that(p.cargo_process("rustdoc").arg("-v")
+                 .arg("--").arg("--no-defaults"),
+                execs()
+                .with_status(101)
+                .with_stderr("Cannot document a package where a library and a \
+                              binary have the same name. Consider renaming one \
+                              or marking the target as `doc = false`"));
+});
index 612d11e0344b886374322d6e20373d1077205bfc..19aaf0afff038b9ddaf0de64b485564282092341 100644 (file)
@@ -57,6 +57,7 @@ mod test_cargo_read_manifest;
 mod test_cargo_registry;
 mod test_cargo_run;
 mod test_cargo_rustc;
+mod test_cargo_rustdoc;
 mod test_cargo_search;
 mod test_cargo_test;
 mod test_cargo_tool_paths;